package com.suyeer.fastwechat.bean.base;

import com.suyeer.basic.util.BasicUtil;
import com.suyeer.basic.util.ExecutorUtil;
import com.suyeer.basic.util.LogUtil;
import com.suyeer.fastwechat.enums.CacheLocationEnum;
import com.suyeer.fastwechat.util.FwUdpUtil;
import com.suyeer.fastwechat.util.FwUtil;

import java.util.HashSet;
import java.util.concurrent.ExecutorService;

/**
 * @author jun 2018/11/22
 */
public class WeChatProperties {

    private static class LazyHolder {
        private static final WeChatProperties INSTANCE = new WeChatProperties(true);
    }

    public static WeChatProperties getInstance() {
        return LazyHolder.INSTANCE;
    }

    private final static String DOMAIN_END_STRING = "/";
    private ExecutorService executorOpenUdpServer = ExecutorUtil.newSingleThreadExecutor("fastwechat-openUdpServer");
    private ExecutorService executorCheckToken = ExecutorUtil.newSingleThreadExecutor("fastwechat-checkToken");

    private WeChatProperties(Boolean flag) {
    }

    /**
     * 此构造函数受 spring bean 注入管理;
     */
    private WeChatProperties() {
        initUdpServer();
        checkToken();
    }

    private void initUdpServer() {
        executorOpenUdpServer.execute(() -> {
            try {
                Thread.sleep(3000);
                if (ifOpenUdpServer) {
                    FwUdpUtil.startUdpServer();
                }
            } catch (Exception e) {
                LogUtil.error(e);
            }
        });
    }

    private void checkToken() {
        executorCheckToken.execute(() -> {
            try {
                Thread.sleep(3000);
                while (ifCheckToken) {
                    FwUtil.getAccessToken();
                    FwUtil.getJsApiTicket();
                    Thread.sleep(checkTokenTime * 1000);
                }
            } catch (Exception e) {
                LogUtil.error(e);
            }
        });
    }

    /**
     * Request: YES;
     * Desc: 微信公众号 appId;
     */
    private static String appId;

    /**
     * Request: YES;
     * Desc: 微信公众号 appSecret;
     */
    private static String appSecret;

    /**
     * Request: NO;
     * Desc: 微信商户开发 mchId, 接入支付时使用;
     */
    private static String mchId;

    /**
     * Request: NO;
     * Desc: 微信商户开发 partnerKey, 接入支付时使用;
     */
    private static String partnerKey;

    /**
     * Request: NO;
     * Desc: 在微信公众号设置的域名, 需指定http 或 https;
     * Example: https://www.suyeer.com
     */
    private static String domain;

    /**
     * Request: NO, 若项目非 负载均衡 项目;
     * Desc: 是否启用缓存来存储微信的全局access token数据, 默认在服务器内缓存,
     * 可用 memcached 或者 redis 缓存数据, 也可以设置不缓存
     * <p>
     * memcached参数配置请参考: http://gitee.com/suyeer/cache/wikis/config
     * <p>
     * JsApiTicket 无需同步到memcached;
     * 当 (ifLocalCache || ifMemCached) == true 时, JsApiTicket会在本地缓存存储;
     * <p>
     * 当取值为 UDP  的时候, 需要配置参数: udpServerIp
     * <p>
     */
    private static CacheLocationEnum cacheLocation = CacheLocationEnum.LOCALHOST;

    /**
     * Request: NO;
     * Desc: JsApiTicket 在微信生成的规则是, 从 00:00 开始, 每隔 2 小时更新一次.
     * 本地也会在这个时间点请求微信获取数据, 但由于本地时间和微信服务器时间存在误差,
     * 可能存在: 本地时间 02:01 请求微信数据并缓存, 但微信服务器时间为 01:59, 此
     * 时缓存的数据可能会有误.
     * 本参数是为解决这个问题. 设值为 360 表示, 本地时间 01:57 ~ 02:03 这 360 秒
     * 内的首次 JsApiTicket 的请求直接从微信服务器获取, 并开启一个新线程任务: 每隔60秒,
     * 系统自动请求微信服务器获取 JsApiTicket 并更新缓存(容错 3 次).
     * 02:03:01 ~ 03:56:59 的请求直接从缓存获取, 若无缓存, 则从微信获取并缓存.
     * <p>
     * 参数取值范围: 0 ~ 1000
     */
    private static Integer ticketCacheRefreshTime = 360;

    /**
     * Request: NO;
     * Desc: 全局access token数据缓存时间, 默认 6000 秒;
     * 当 (ifLocalCache || ifMemCached) == true 时, 此参数有效;
     */
    private static Integer accessTokenActiveTime = 6000;

    /**
     * Request: NO;
     * Desc: 指定缓存key值前缀, 如果有多个系统共用一个公众号且共用一个缓存服务器, 则配置相同的值;
     */
    private static String cachePrefixKey = "";

    /**
     * Request: NO;
     * Desc: 是否检查 AccessToken 和 ticket, 默认每 checkTokenTime 秒检查一次;
     */
    private static Boolean ifCheckToken = true;

    /**
     * Request: NO;
     * Desc: 检查 AccessToken 和 ticket 的间隔, 秒, 默认 1 分钟检查一次;
     */
    private static int checkTokenTime = 60;

    /**
     * Request: NO;
     * Desc: 是否开启 udp 传输全局 AccessToken 和 JsApiTicket;
     */
    private static Boolean ifOpenUdpServer = false;

    /**
     * Request: NO;
     * Desc: 开启 udp 的端口号;
     * 若此端口号被占用, 则会 数值 +1 尝试, 共尝试 3 次;
     */
    private static Integer udpPort = 4869;

    /**
     * Request: NO;
     * Desc: 开启 udp后, 允许客户端的ip地址, 多个地址可以用逗号(,)隔开, 默认允许所有ip;
     * 如:  192.168.1.2,192.168.1.3,192.168.1.4
     */
    private static String udpClientIp = "";

    /**
     * 提供 UDP 服务的主机ip
     */
    private static String udpServerIp = "";

    public String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        WeChatProperties.appId = appId;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public void setAppSecret(String appSecret) {
        WeChatProperties.appSecret = appSecret;
    }

    public String getMchId() {
        return mchId;
    }

    public void setMchId(String mchId) {
        WeChatProperties.mchId = mchId;
    }

    public String getPartnerKey() {
        return partnerKey;
    }

    public void setPartnerKey(String partnerKey) {
        WeChatProperties.partnerKey = partnerKey;
    }

    public String getDomain() {
        if (domain != null && domain.endsWith(DOMAIN_END_STRING)) {
            domain = domain.substring(0, domain.length() - 1);
        }
        return domain;
    }

    public void setDomain(String domain) {
        WeChatProperties.domain = domain;
    }

    public String getCachePrefixKey() {
        return cachePrefixKey;
    }

    public void setCachePrefixKey(String cachePrefixKey) {
        WeChatProperties.cachePrefixKey = cachePrefixKey;
    }

    public Integer getTicketCacheRefreshTime() {
        ticketCacheRefreshTime = Math.max(ticketCacheRefreshTime, 0);
        ticketCacheRefreshTime = Math.min(ticketCacheRefreshTime, 1000);
        return ticketCacheRefreshTime / 2;
    }

    public void setTicketCacheRefreshTime(Integer ticketCacheRefreshTime) {
        WeChatProperties.ticketCacheRefreshTime = ticketCacheRefreshTime;
    }

    public Integer getAccessTokenActiveTime() {
        return accessTokenActiveTime;
    }

    public void setAccessTokenActiveTime(Integer accessTokenActiveTime) {
        WeChatProperties.accessTokenActiveTime = accessTokenActiveTime;
    }

    public Boolean getIfOpenUdpServer() {
        return ifOpenUdpServer;
    }

    public void setIfOpenUdpServer(Boolean ifOpenUdpServer) {
        WeChatProperties.ifOpenUdpServer = ifOpenUdpServer;
    }

    public String getUdpClientIp() {
        return udpClientIp;
    }

    public void setUdpClientIp(String udpClientIp) {
        WeChatProperties.udpClientIp = udpClientIp;
    }

    public HashSet<String> getUdpClientIpList() {
        HashSet<String> set = new HashSet<>();
        set.addAll(BasicUtil.getList(udpClientIp));
        return set;
    }

    public String getUdpServerIp() {
        return udpServerIp;
    }

    public void setUdpServerIp(String udpServerIp) {
        WeChatProperties.udpServerIp = udpServerIp;
    }

    public HashSet<String> getUdpServerIpList() {
        HashSet<String> set = new HashSet<>();
        set.addAll(BasicUtil.getList(udpServerIp));
        return set;
    }

    public Integer getUdpPort() {
        return udpPort;
    }

    public void setUdpPort(Integer udpPort) {
        WeChatProperties.udpPort = udpPort;
    }

    public Boolean getIfCheckToken() {
        return ifCheckToken;
    }

    public void setIfCheckToken(Boolean ifCheckToken) {
        WeChatProperties.ifCheckToken = ifCheckToken;
    }

    public int getCheckTokenTime() {
        return checkTokenTime;
    }

    public void setCheckTokenTime(int checkTokenTime) {
        WeChatProperties.checkTokenTime = checkTokenTime;
    }

    public CacheLocationEnum getCacheLocation() {
        return cacheLocation;
    }

    public void setCacheLocation(CacheLocationEnum cacheLocation) {
        WeChatProperties.cacheLocation = cacheLocation;
    }
}
